package lang.asm;

import model.code.Function;
import model.code.line.Immediate;
import model.code.line.MemoryOperand;
import model.code.line.SyntaxError;
import model.symbol.*;

import java.util.ArrayList;

/**
 * This is a utilities class for base assembly language data types, including registers, immediates, and memory types.
 *
 * @author Neil Dickson
 */
public abstract class DataTypeUtil {
	public static final int TYPE_INDEX_RG8			= 0;
	public static final int FIRST_REG_TYPE			= TYPE_INDEX_RG8;
	public static final int TYPE_INDEX_RG8_32		= TYPE_INDEX_RG8+1;
	public static final int TYPE_INDEX_RG8_64		= TYPE_INDEX_RG8_32+1;
	public static final int TYPE_INDEX_RG8_32_64	= TYPE_INDEX_RG8_64+1;
	public static final int TYPE_INDEX_RG16			= TYPE_INDEX_RG8_32_64+1;
	public static final int TYPE_INDEX_RG16_32		= TYPE_INDEX_RG16+1;
	public static final int TYPE_INDEX_RG16_64		= TYPE_INDEX_RG16_32+1;
	public static final int TYPE_INDEX_RG16_32_64	= TYPE_INDEX_RG16_64+1;
	public static final int TYPE_INDEX_RG32			= TYPE_INDEX_RG16_32_64+1;
	public static final int TYPE_INDEX_RG32_32		= TYPE_INDEX_RG32+1;
	public static final int TYPE_INDEX_RG32_64		= TYPE_INDEX_RG32_32+1;
	public static final int TYPE_INDEX_RG32_32_64	= TYPE_INDEX_RG32_64+1;
	public static final int TYPE_INDEX_RG64			= TYPE_INDEX_RG32_32_64+1;
	public static final int TYPE_INDEX_RGFPU		= TYPE_INDEX_RG64+1;
	public static final int TYPE_INDEX_RGMMX		= TYPE_INDEX_RGFPU+1;
	public static final int TYPE_INDEX_RGSSE		= TYPE_INDEX_RGMMX+1;
	public static final int TYPE_INDEX_RGSSE_32		= TYPE_INDEX_RGSSE+1;
	public static final int TYPE_INDEX_RGSSE_64		= TYPE_INDEX_RGSSE_32+1;
	public static final int TYPE_INDEX_RGSSE_32_64	= TYPE_INDEX_RGSSE_64+1;
	public static final int TYPE_INDEX_SR			= TYPE_INDEX_RGSSE_32_64+1;
	public static final int TYPE_INDEX_CR			= TYPE_INDEX_SR+1;
	public static final int TYPE_INDEX_CR_32		= TYPE_INDEX_CR+1;
	public static final int TYPE_INDEX_CR_64		= TYPE_INDEX_CR_32+1;
	public static final int TYPE_INDEX_CR_32_64		= TYPE_INDEX_CR_64+1;
	public static final int TYPE_INDEX_DR			= TYPE_INDEX_CR_32_64+1;
	public static final int TYPE_INDEX_RIP			= TYPE_INDEX_DR+1;
	public static final int LAST_REG_TYPE			= TYPE_INDEX_RIP;
	public static final int TYPE_INDEX_IMM8			= TYPE_INDEX_RIP+1;
	public static final int FIRST_IMM_TYPE			= TYPE_INDEX_IMM8;
	/**
	 * NOTE: {@link MemoryOperand#MATCH_IMM8S} requires TYPE_INDEX_IMM8S to be at least 13.
	 * NOTE: {@link MemoryOperand#findMatchEncoding(int)} requires TYPE_INDEX_IMM8S to be the smallest immediate type index other than TYPE_INDEX_IMM8.
	 */
	public static final int TYPE_INDEX_IMM8S		= TYPE_INDEX_IMM8+1;
	public static final int TYPE_INDEX_IMM8U		= TYPE_INDEX_IMM8S+1;
	public static final int TYPE_INDEX_IMM8SU		= TYPE_INDEX_IMM8U+1;
	public static final int TYPE_INDEX_IMM8_3F		= TYPE_INDEX_IMM8SU+1;
	public static final int TYPE_INDEX_IMM8_1F		= TYPE_INDEX_IMM8_3F+1;
	public static final int TYPE_INDEX_IMM8_F		= TYPE_INDEX_IMM8_1F+1;
	public static final int TYPE_INDEX_IMM8_7		= TYPE_INDEX_IMM8_F+1;
	public static final int TYPE_INDEX_IMM8_3		= TYPE_INDEX_IMM8_7+1;
	public static final int TYPE_INDEX_IMM8_1		= TYPE_INDEX_IMM8_3+1;
	public static final int TYPE_INDEX_IMM16		= TYPE_INDEX_IMM8_1+1;
	public static final int TYPE_INDEX_IMM16S		= TYPE_INDEX_IMM16+1;
	public static final int TYPE_INDEX_IMM16U		= TYPE_INDEX_IMM16S+1;
	public static final int TYPE_INDEX_IMM16SU		= TYPE_INDEX_IMM16U+1;
	public static final int TYPE_INDEX_IMM32		= TYPE_INDEX_IMM16SU+1;
	public static final int TYPE_INDEX_IMM32S		= TYPE_INDEX_IMM32+1;
	public static final int TYPE_INDEX_IMM32U		= TYPE_INDEX_IMM32S+1;
	public static final int TYPE_INDEX_IMM32SU		= TYPE_INDEX_IMM32U+1;
	public static final int TYPE_INDEX_IMM64		= TYPE_INDEX_IMM32SU+1;
	public static final int LAST_IMM_TYPE			= TYPE_INDEX_IMM64;
	public static final int TYPE_INDEX_M			= TYPE_INDEX_IMM64+1;
	public static final int FIRST_MEM_TYPE			= TYPE_INDEX_M;
	public static final int TYPE_INDEX_M_32			= TYPE_INDEX_M+1;
	public static final int TYPE_INDEX_M_64			= TYPE_INDEX_M_32+1;
	public static final int TYPE_INDEX_MUNKNOWN_32	= TYPE_INDEX_M_64+1;
	public static final int TYPE_INDEX_MUNKNOWN_64	= TYPE_INDEX_MUNKNOWN_32+1;
	public static final int TYPE_INDEX_M8_32		= TYPE_INDEX_MUNKNOWN_64+1;
	public static final int TYPE_INDEX_M8_64		= TYPE_INDEX_M8_32+1;
	public static final int TYPE_INDEX_M16_32		= TYPE_INDEX_M8_64+1;
	public static final int TYPE_INDEX_M16_64		= TYPE_INDEX_M16_32+1;
	public static final int TYPE_INDEX_M32_32		= TYPE_INDEX_M16_64+1;
	public static final int TYPE_INDEX_M32_64		= TYPE_INDEX_M32_32+1;
	public static final int TYPE_INDEX_M48_32		= TYPE_INDEX_M32_64+1;
	public static final int TYPE_INDEX_M48_64		= TYPE_INDEX_M48_32+1;
	public static final int TYPE_INDEX_M64_32		= TYPE_INDEX_M48_64+1;
	public static final int TYPE_INDEX_M64_64		= TYPE_INDEX_M64_32+1;
	public static final int TYPE_INDEX_M80_32		= TYPE_INDEX_M64_64+1;
	public static final int TYPE_INDEX_M80_64		= TYPE_INDEX_M80_32+1;
	public static final int TYPE_INDEX_M128_32		= TYPE_INDEX_M80_64+1;
	public static final int TYPE_INDEX_M128_64		= TYPE_INDEX_M128_32+1;
	public static final int TYPE_INDEX_MX_32		= TYPE_INDEX_M128_64+1;
	public static final int TYPE_INDEX_MX_64		= TYPE_INDEX_MX_32+1;
	public static final int LAST_MEM_TYPE			= TYPE_INDEX_MX_64;
	public static final int TYPE_INDEX_RM8_32		= TYPE_INDEX_MX_64+1;
	public static final int FIRST_RM_TYPE			= TYPE_INDEX_RM8_32;
	public static final int TYPE_INDEX_RM8_64		= TYPE_INDEX_RM8_32+1;
	public static final int TYPE_INDEX_RM16_32		= TYPE_INDEX_RM8_64+1;
	public static final int TYPE_INDEX_RM16_64		= TYPE_INDEX_RM16_32+1;
	public static final int TYPE_INDEX_RM32_32		= TYPE_INDEX_RM16_64+1;
	public static final int TYPE_INDEX_RM32_64		= TYPE_INDEX_RM32_32+1;
	public static final int TYPE_INDEX_RM64			= TYPE_INDEX_RM32_64+1;
	public static final int TYPE_INDEX_RMMMX_32		= TYPE_INDEX_RM64+1;
	public static final int TYPE_INDEX_RMMMX_64		= TYPE_INDEX_RMMMX_32+1;
	public static final int TYPE_INDEX_RMSSE_32		= TYPE_INDEX_RMMMX_64+1;
	public static final int TYPE_INDEX_RMSSE_64		= TYPE_INDEX_RMSSE_32+1;
	public static final int LAST_RM_TYPE			= TYPE_INDEX_RMSSE_64;
	public static final int TYPE_INDEX_SHORT_LABEL	= TYPE_INDEX_RMSSE_64+1;
	public static final int TYPE_INDEX_NEAR_LABEL	= TYPE_INDEX_SHORT_LABEL+1;
	public static final int TYPE_INDEX_F16			= TYPE_INDEX_NEAR_LABEL+1;
	public static final int TYPE_INDEX_F32			= TYPE_INDEX_F16+1;
	public static final int TYPE_INDEX_F64			= TYPE_INDEX_F32+1;
	public static final int TYPE_INDEX_UNKNOWN		= TYPE_INDEX_F64+1;
	
	public static final TypeMapping[] TYPE_MAP = {
		new TypeMapping("rg8",8,new int[]{TYPE_INDEX_RG8_64,TYPE_INDEX_RG8_32_64,TYPE_INDEX_RG8_32}),
		new TypeMapping("rg8_32",8,new int[]{TYPE_INDEX_RG8_32_64}),
		new TypeMapping("rg8_64",8,new int[]{TYPE_INDEX_RG8_32_64}),
		new TypeMapping("rg8_32&rg8_64",8,null),
		new TypeMapping("rg16",16,new int[]{TYPE_INDEX_RG16_64,TYPE_INDEX_RG16_32_64,TYPE_INDEX_RG16_32}),
		new TypeMapping("rg16_32",16,new int[]{TYPE_INDEX_RG16_32_64}),
		new TypeMapping("rg16_64",16,new int[]{TYPE_INDEX_RG16_32_64}),
		new TypeMapping("rg16_32&rg16_64",16,null),
		new TypeMapping("rg32",32,new int[]{TYPE_INDEX_RG32_64,TYPE_INDEX_RG32_32_64,TYPE_INDEX_RG32_32}),
		new TypeMapping("rg32_32",32,new int[]{TYPE_INDEX_RG32_32_64}),
		new TypeMapping("rg32_64",32,new int[]{TYPE_INDEX_RG32_32_64}),
		new TypeMapping("rg32_32&rg32_64",32,null),
		new TypeMapping("rg64",64,null),
		new TypeMapping("rgfpu",80,null),
		new TypeMapping("rgmmx",64,null),
		new TypeMapping("rgsse",128,new int[]{TYPE_INDEX_RGSSE_32_64,TYPE_INDEX_RGSSE_64,TYPE_INDEX_RGSSE_32}),
		new TypeMapping("rgsse_32",128,new int[]{TYPE_INDEX_RGSSE_32_64}),
		new TypeMapping("rgsse_64",128,new int[]{TYPE_INDEX_RGSSE_32_64}),
		new TypeMapping("rgsse_32&rgsse_64",128,null),
		new TypeMapping("sr",16,null),
		new TypeMapping("cr",new int[]{TYPE_INDEX_CR_32_64,TYPE_INDEX_CR_64,TYPE_INDEX_CR_32}),
		new TypeMapping("cr_32",new int[]{TYPE_INDEX_CR_32_64}),
		new TypeMapping("cr_64",new int[]{TYPE_INDEX_CR_32_64}),
		new TypeMapping("cr_32&cr_64",null),
		new TypeMapping("dr",null),
		new TypeMapping("rip",null),
		new TypeMapping("imm8",8,new int[]{TYPE_INDEX_IMM8S,TYPE_INDEX_IMM8U,TYPE_INDEX_IMM8SU,TYPE_INDEX_IMM8_3F,TYPE_INDEX_IMM8_1F,TYPE_INDEX_IMM8_F,TYPE_INDEX_IMM8_7,TYPE_INDEX_IMM8_3,TYPE_INDEX_IMM8_1}),
		new TypeMapping("imm8s",8,new int[]{TYPE_INDEX_IMM8SU,TYPE_INDEX_IMM8_3F,TYPE_INDEX_IMM8_1F,TYPE_INDEX_IMM8_F,TYPE_INDEX_IMM8_7,TYPE_INDEX_IMM8_3,TYPE_INDEX_IMM8_1}),
		new TypeMapping("imm8u",8,new int[]{TYPE_INDEX_IMM8SU,TYPE_INDEX_IMM8_3F,TYPE_INDEX_IMM8_1F,TYPE_INDEX_IMM8_F,TYPE_INDEX_IMM8_7,TYPE_INDEX_IMM8_3,TYPE_INDEX_IMM8_1}),
		new TypeMapping("imm8su",8,new int[]{TYPE_INDEX_IMM8_3F,TYPE_INDEX_IMM8_1F,TYPE_INDEX_IMM8_F,TYPE_INDEX_IMM8_7,TYPE_INDEX_IMM8_3,TYPE_INDEX_IMM8_1}),
		new TypeMapping("imm8(00-3F)",8,new int[]{TYPE_INDEX_IMM8_1F,TYPE_INDEX_IMM8_F,TYPE_INDEX_IMM8_7,TYPE_INDEX_IMM8_3,TYPE_INDEX_IMM8_1}),
		new TypeMapping("imm8(00-1F)",8,new int[]{TYPE_INDEX_IMM8_F,TYPE_INDEX_IMM8_7,TYPE_INDEX_IMM8_3,TYPE_INDEX_IMM8_1}),
		new TypeMapping("imm8(00-0F)",8,new int[]{TYPE_INDEX_IMM8_7,TYPE_INDEX_IMM8_3,TYPE_INDEX_IMM8_1}),
		new TypeMapping("imm8(00-07)",8,new int[]{TYPE_INDEX_IMM8_3,TYPE_INDEX_IMM8_1}),
		new TypeMapping("imm8(00-03)",8,new int[]{TYPE_INDEX_IMM8_1}),
		new TypeMapping("imm8(00-01)",8,null),
		new TypeMapping("imm16",16,new int[]{TYPE_INDEX_IMM16S,TYPE_INDEX_IMM16U,TYPE_INDEX_IMM16SU,TYPE_INDEX_IMM8,TYPE_INDEX_IMM8S,TYPE_INDEX_IMM8U,TYPE_INDEX_IMM8SU,TYPE_INDEX_IMM8_3F,TYPE_INDEX_IMM8_1F,TYPE_INDEX_IMM8_F,TYPE_INDEX_IMM8_7,TYPE_INDEX_IMM8_3,TYPE_INDEX_IMM8_1}),
		new TypeMapping("imm16s",16,new int[]{TYPE_INDEX_IMM16SU,TYPE_INDEX_IMM8,TYPE_INDEX_IMM8S,TYPE_INDEX_IMM8U,TYPE_INDEX_IMM8SU,TYPE_INDEX_IMM8_3F,TYPE_INDEX_IMM8_1F,TYPE_INDEX_IMM8_F,TYPE_INDEX_IMM8_7,TYPE_INDEX_IMM8_3,TYPE_INDEX_IMM8_1}),
		new TypeMapping("imm16u",16,new int[]{TYPE_INDEX_IMM16SU,TYPE_INDEX_IMM8,TYPE_INDEX_IMM8S,TYPE_INDEX_IMM8U,TYPE_INDEX_IMM8SU,TYPE_INDEX_IMM8_3F,TYPE_INDEX_IMM8_1F,TYPE_INDEX_IMM8_F,TYPE_INDEX_IMM8_7,TYPE_INDEX_IMM8_3,TYPE_INDEX_IMM8_1}),
		new TypeMapping("imm16su",16,new int[]{TYPE_INDEX_IMM8,TYPE_INDEX_IMM8S,TYPE_INDEX_IMM8U,TYPE_INDEX_IMM8SU,TYPE_INDEX_IMM8_3F,TYPE_INDEX_IMM8_1F,TYPE_INDEX_IMM8_F,TYPE_INDEX_IMM8_7,TYPE_INDEX_IMM8_3,TYPE_INDEX_IMM8_1}),
		new TypeMapping("imm32",32,new int[]{TYPE_INDEX_IMM32S,TYPE_INDEX_IMM32U,TYPE_INDEX_IMM32SU,TYPE_INDEX_IMM16,TYPE_INDEX_IMM16S,TYPE_INDEX_IMM16U,TYPE_INDEX_IMM16SU,TYPE_INDEX_IMM8,TYPE_INDEX_IMM8S,TYPE_INDEX_IMM8U,TYPE_INDEX_IMM8SU,TYPE_INDEX_IMM8_3F,TYPE_INDEX_IMM8_1F,TYPE_INDEX_IMM8_F,TYPE_INDEX_IMM8_7,TYPE_INDEX_IMM8_3,TYPE_INDEX_IMM8_1}),
		new TypeMapping("imm32s",32,new int[]{TYPE_INDEX_IMM32SU,TYPE_INDEX_IMM16,TYPE_INDEX_IMM16S,TYPE_INDEX_IMM16U,TYPE_INDEX_IMM16SU,TYPE_INDEX_IMM8,TYPE_INDEX_IMM8S,TYPE_INDEX_IMM8U,TYPE_INDEX_IMM8SU,TYPE_INDEX_IMM8_3F,TYPE_INDEX_IMM8_1F,TYPE_INDEX_IMM8_F,TYPE_INDEX_IMM8_7,TYPE_INDEX_IMM8_3,TYPE_INDEX_IMM8_1}),
		new TypeMapping("imm32u",32,new int[]{TYPE_INDEX_IMM32SU,TYPE_INDEX_IMM16,TYPE_INDEX_IMM16S,TYPE_INDEX_IMM16U,TYPE_INDEX_IMM16SU,TYPE_INDEX_IMM8,TYPE_INDEX_IMM8S,TYPE_INDEX_IMM8U,TYPE_INDEX_IMM8SU,TYPE_INDEX_IMM8_3F,TYPE_INDEX_IMM8_1F,TYPE_INDEX_IMM8_F,TYPE_INDEX_IMM8_7,TYPE_INDEX_IMM8_3,TYPE_INDEX_IMM8_1}),
		new TypeMapping("imm32su",32,new int[]{TYPE_INDEX_IMM16,TYPE_INDEX_IMM16S,TYPE_INDEX_IMM16U,TYPE_INDEX_IMM16SU,TYPE_INDEX_IMM8,TYPE_INDEX_IMM8S,TYPE_INDEX_IMM8U,TYPE_INDEX_IMM8SU,TYPE_INDEX_IMM8_3F,TYPE_INDEX_IMM8_1F,TYPE_INDEX_IMM8_F,TYPE_INDEX_IMM8_7,TYPE_INDEX_IMM8_3,TYPE_INDEX_IMM8_1}),
		new TypeMapping("imm64",64,new int[]{TYPE_INDEX_IMM32,TYPE_INDEX_IMM32S,TYPE_INDEX_IMM32U,TYPE_INDEX_IMM32SU,TYPE_INDEX_IMM16,TYPE_INDEX_IMM16S,TYPE_INDEX_IMM16U,TYPE_INDEX_IMM16SU,TYPE_INDEX_IMM8,TYPE_INDEX_IMM8S,TYPE_INDEX_IMM8U,TYPE_INDEX_IMM8SU,TYPE_INDEX_IMM8_3F,TYPE_INDEX_IMM8_1F,TYPE_INDEX_IMM8_F,TYPE_INDEX_IMM8_7,TYPE_INDEX_IMM8_3,TYPE_INDEX_IMM8_1}),
		new TypeMapping("m",new int[]{TYPE_INDEX_M8_32,TYPE_INDEX_M16_32,TYPE_INDEX_M32_32,TYPE_INDEX_M48_32,TYPE_INDEX_M64_32,TYPE_INDEX_M80_32,TYPE_INDEX_M128_32,TYPE_INDEX_MX_32,TYPE_INDEX_MUNKNOWN_32,TYPE_INDEX_M8_64,TYPE_INDEX_M16_64,TYPE_INDEX_M32_64,TYPE_INDEX_M48_64,TYPE_INDEX_M64_64,TYPE_INDEX_M80_64,TYPE_INDEX_M128_64,TYPE_INDEX_MX_64,TYPE_INDEX_MUNKNOWN_64}),
		new TypeMapping("m_32",new int[]{TYPE_INDEX_M8_32,TYPE_INDEX_M16_32,TYPE_INDEX_M32_32,TYPE_INDEX_M48_32,TYPE_INDEX_M64_32,TYPE_INDEX_M80_32,TYPE_INDEX_M128_32,TYPE_INDEX_MX_32,TYPE_INDEX_MUNKNOWN_32}),
		// NOTE: m_64 is equivalent to m
		new TypeMapping("m_64",new int[]{TYPE_INDEX_M8_64,TYPE_INDEX_M16_64,TYPE_INDEX_M32_64,TYPE_INDEX_M48_64,TYPE_INDEX_M64_64,TYPE_INDEX_M80_64,TYPE_INDEX_M128_64,TYPE_INDEX_MX_64,TYPE_INDEX_MUNKNOWN_64,TYPE_INDEX_M8_32,TYPE_INDEX_M16_32,TYPE_INDEX_M32_32,TYPE_INDEX_M48_32,TYPE_INDEX_M64_32,TYPE_INDEX_M80_32,TYPE_INDEX_M128_32,TYPE_INDEX_MX_32,TYPE_INDEX_MUNKNOWN_32}),
		new TypeMapping("munknown_32",null),	// NOTE: Not used in language definition; only for when memory operand of unknown size.
		new TypeMapping("munknown_64",null),	// NOTE: Not used in language definition; only for when memory operand of unknown size.
		new TypeMapping("m8_32",8,new int[]{TYPE_INDEX_MUNKNOWN_32}),
		new TypeMapping("m8_64",8,new int[]{TYPE_INDEX_M8_32,TYPE_INDEX_MUNKNOWN_32,TYPE_INDEX_MUNKNOWN_64}),
		new TypeMapping("m16_32",16,new int[]{TYPE_INDEX_MUNKNOWN_32}),
		new TypeMapping("m16_64",16,new int[]{TYPE_INDEX_M16_32,TYPE_INDEX_MUNKNOWN_32,TYPE_INDEX_MUNKNOWN_64}),
		new TypeMapping("m32_32",32,new int[]{TYPE_INDEX_MUNKNOWN_32}),
		new TypeMapping("m32_64",32,new int[]{TYPE_INDEX_M32_32,TYPE_INDEX_MUNKNOWN_32,TYPE_INDEX_MUNKNOWN_64}),
		new TypeMapping("m48_32",48,new int[]{TYPE_INDEX_MUNKNOWN_32}),
		new TypeMapping("m48_64",48,new int[]{TYPE_INDEX_M48_32,TYPE_INDEX_MUNKNOWN_32,TYPE_INDEX_MUNKNOWN_64}),
		new TypeMapping("m64_32",64,new int[]{TYPE_INDEX_MUNKNOWN_32}),
		new TypeMapping("m64_64",64,new int[]{TYPE_INDEX_M64_32,TYPE_INDEX_MUNKNOWN_32,TYPE_INDEX_MUNKNOWN_64}),
		new TypeMapping("m80_32",80,new int[]{TYPE_INDEX_MUNKNOWN_32}),
		new TypeMapping("m80_64",80,new int[]{TYPE_INDEX_M80_32,TYPE_INDEX_MUNKNOWN_32,TYPE_INDEX_MUNKNOWN_64}),
		new TypeMapping("m128_32",128,new int[]{TYPE_INDEX_MUNKNOWN_32}),
		new TypeMapping("m128_64",128,new int[]{TYPE_INDEX_M128_32,TYPE_INDEX_MUNKNOWN_32,TYPE_INDEX_MUNKNOWN_64}),
		// NOTE: The next two are for when the size of memory is known but not a standard size.
		new TypeMapping("mx_32",new int[]{TYPE_INDEX_MUNKNOWN_32}),
		new TypeMapping("mx_64",new int[]{TYPE_INDEX_MX_32,TYPE_INDEX_MUNKNOWN_32,TYPE_INDEX_MUNKNOWN_64}),
		new TypeMapping("rm8_32",8,new int[]{TYPE_INDEX_RG8_32,TYPE_INDEX_RG8_32_64,TYPE_INDEX_M8_32,TYPE_INDEX_MUNKNOWN_32}),
		new TypeMapping("rm8_64",8,new int[]{TYPE_INDEX_RG8_64,TYPE_INDEX_RG8_32_64,TYPE_INDEX_M8_64,TYPE_INDEX_M8_32,TYPE_INDEX_RM8_32,TYPE_INDEX_MUNKNOWN_32,TYPE_INDEX_MUNKNOWN_64}),
		new TypeMapping("rm16_32",16,new int[]{TYPE_INDEX_RG16_32,TYPE_INDEX_RG16_32_64,TYPE_INDEX_M16_32,TYPE_INDEX_MUNKNOWN_32}),
		new TypeMapping("rm16_64",16,new int[]{TYPE_INDEX_RG16_32,TYPE_INDEX_RG16_64,TYPE_INDEX_RG16_32_64,TYPE_INDEX_M16_64,TYPE_INDEX_M16_32,TYPE_INDEX_RM16_32,TYPE_INDEX_MUNKNOWN_32,TYPE_INDEX_MUNKNOWN_64}),
		new TypeMapping("rm32_32",32,new int[]{TYPE_INDEX_RG32_32,TYPE_INDEX_RG32_32_64,TYPE_INDEX_M32_32,TYPE_INDEX_MUNKNOWN_32}),
		new TypeMapping("rm32_64",32,new int[]{TYPE_INDEX_RG32_32,TYPE_INDEX_RG32_64,TYPE_INDEX_RG32_32_64,TYPE_INDEX_M32_64,TYPE_INDEX_M32_32,TYPE_INDEX_RM32_32,TYPE_INDEX_MUNKNOWN_32,TYPE_INDEX_MUNKNOWN_64}),
		new TypeMapping("rm64",64,new int[]{TYPE_INDEX_RG64,TYPE_INDEX_M64_64,TYPE_INDEX_M64_32,TYPE_INDEX_MUNKNOWN_32,TYPE_INDEX_MUNKNOWN_64}),
		new TypeMapping("rmmmx_32",64,new int[]{TYPE_INDEX_RGMMX,TYPE_INDEX_M64_32,TYPE_INDEX_MUNKNOWN_32}),
		new TypeMapping("rmmmx_64",64,new int[]{TYPE_INDEX_RGMMX,TYPE_INDEX_M64_64,TYPE_INDEX_M64_32,TYPE_INDEX_RMMMX_32,TYPE_INDEX_MUNKNOWN_32,TYPE_INDEX_MUNKNOWN_64}),
		new TypeMapping("rmsse_32",128,new int[]{TYPE_INDEX_RGSSE_32,TYPE_INDEX_RGSSE_32_64,TYPE_INDEX_M128_32,TYPE_INDEX_MUNKNOWN_32}),
		new TypeMapping("rmsse_64",128,new int[]{TYPE_INDEX_RGSSE_32,TYPE_INDEX_RGSSE_64,TYPE_INDEX_RGSSE_32_64,TYPE_INDEX_M128_64,TYPE_INDEX_M128_32,TYPE_INDEX_RMSSE_32,TYPE_INDEX_MUNKNOWN_32,TYPE_INDEX_MUNKNOWN_64}),
		new TypeMapping("short line_label",8,null),
		new TypeMapping("near line_label",null),
		new TypeMapping("function_16",16,null),
		new TypeMapping("function_32",32,null),
		new TypeMapping("function_64",64,null),
		new TypeMapping("unknown",null)
	};

	/**
	 * Private constructor to prevent instantiation
	 */
	private DataTypeUtil() {}

	/**
	 * Determines the DataType indicated by the specified string in the specified context.
	 * @param type the type string to examine
	 * @param languageScope the relevant language
	 * @param globalScope the global scope
	 * @param currentMode the relevant mode (16, 32, or 64)
	 * @return the DataType represented by type or null if unknown
	 */
	public static DataType getDataType(String type,Scope languageScope,Scope globalScope,int currentMode) {
		String typeLower = type.toLowerCase();
		if (typeLower.startsWith("ptr ") || typeLower.equals("ptr")) {
			if (currentMode==64) {
				return ASM.QWORD;
			}
			if (currentMode==32) {
				return ASM.DWORD;
			}
			// currentMode==16
			return ASM.WORD;
		}
		ArrayList<Symbol> languageType = (languageScope!=null) ? languageScope.get(typeLower) : null;
		DataType dataType = null;
		if (languageType!=null && languageType.size()>0) {
			for (Symbol symbol : languageType) {
				if (symbol instanceof DataType) {
					dataType = (DataType)symbol;
					break;
				}
			}
		}
		if (dataType==null) {
			ArrayList<Symbol> globalType = (globalScope!=null) ? globalScope.get(type) : null;
			if (globalType!=null && globalType.size()>0) {
				for (Symbol symbol : globalType) {
					if (symbol instanceof DataType) {
						dataType = (DataType)symbol;
						break;
					}
				}
			}
		}
		return dataType;
	}
	public static int getOperandTypeFromName(String name) {
		for (int i=0;i<TYPE_MAP.length;++i) {
			if (TYPE_MAP[i].name.equals(name)) {
				return i;
			}
		}
		return TYPE_INDEX_UNKNOWN;
	}
	public static int getOperandType(Object operand,int currentMode) {
		if (operand instanceof Register) {
			return ((Register)operand).getType();
		}
		if (operand instanceof Function) {
			int mode = ((Function)operand).getMode();
			if (mode==64) {
				return TYPE_INDEX_F64;
			}
			if (mode==32) {
				return TYPE_INDEX_F32;
			}
			// mode==16
			return TYPE_INDEX_F16;
		}
		if (operand instanceof LineLabel) {
			// NOTE: Assume short jump first.
			return TYPE_INDEX_SHORT_LABEL;
		}
		else if (operand instanceof MemoryOperand) {
			MemoryOperand mop = (MemoryOperand)operand;
			DataType dataType = mop.type;
			boolean needs64bitPrefix = (mop.regularIndexReg!=null && (mop.regularIndexReg.getNumber()>=Register.THRESHOLD_64_BIT)
									|| (mop.scaledIndexReg!=null && mop.scaledIndexReg.getNumber()>=Register.THRESHOLD_64_BIT));
			int type = getOperandType(dataType);
			if (type==TYPE_INDEX_UNKNOWN) {
				type = TYPE_INDEX_MUNKNOWN_32;
			}
			if (needs64bitPrefix) {
				++type;
			}
			return type;
		}
		else if (operand instanceof SyntaxError) {
			return TYPE_INDEX_UNKNOWN;
		}
		else { // Immediate
			if (((Immediate)operand).relocation!=null || ((Immediate)operand).diffRelocation!=null) {
				// TODO: Determine whether this should check the mode where it's used, or the mode of the symbol being referenced by the relocation.
				if (currentMode==64) {
					// TODO: Find a better way of handling that most encodings for addresses only support 32-bit offsets.
					// The only cases that support 64-bit immediates are: "mov rg64,imm64", "mov rax,m64imm", and "mov m64imm,rax".
					return TYPE_INDEX_IMM32S;
				}
				if (currentMode==32) {
					return TYPE_INDEX_IMM32U;
				}
				// currentMode==16
				return TYPE_INDEX_IMM16U;
			}
			else if (((Immediate)operand).isDouble) {
				// Call it a 32-bit immediate type so that Real4's and Real8's are handled
				return TYPE_INDEX_IMM32;
			}
			else { // immediate with only integer constant
				return getOperandType(((Immediate)operand).constant);
			}
		}
	}
	public static int getOperandType(DataType dataType) {
		if (dataType==null) {
			return TYPE_INDEX_UNKNOWN;
		}
		int size = dataType.getSize();
		switch (size) {
			case 1: return TYPE_INDEX_M8_32;
			case 2: return TYPE_INDEX_M16_32;
			case 4: return TYPE_INDEX_M32_32;
			case 6: return TYPE_INDEX_M48_32;
			case 8: return TYPE_INDEX_M64_32;
			case 10: return TYPE_INDEX_M80_32;
			case 16: return TYPE_INDEX_M128_32;
		}
		return TYPE_INDEX_MX_32;
	}

	public static int getOperandType(long immediate) {
		if (immediate>=0) {
			if (immediate<0x02) {
				return TYPE_INDEX_IMM8_1;
			}
			if (immediate<0x04) {
				return TYPE_INDEX_IMM8_3;
			}
			if (immediate<0x08) {
				return TYPE_INDEX_IMM8_7;
			}
			if (immediate<0x10) {
				return TYPE_INDEX_IMM8_F;
			}
			if (immediate<0x20) {
				return TYPE_INDEX_IMM8_1F;
			}
			if (immediate<0x40) {
				return TYPE_INDEX_IMM8_3F;
			}
			if (immediate<0x80) {
				return TYPE_INDEX_IMM8SU;
			}
			if (immediate<0x100) {
				return TYPE_INDEX_IMM8U;
			}
			if (immediate<0x8000) {
				return TYPE_INDEX_IMM16SU;
			}
			if (immediate<0x10000) {
				return TYPE_INDEX_IMM16U;
			}
			if (immediate<0x80000000L) {
				return TYPE_INDEX_IMM32SU;
			}
			if (immediate<0x100000000L) {
				return TYPE_INDEX_IMM32U;
			}
		}
		else {
			if (immediate>=-0x80) {
				return TYPE_INDEX_IMM8S;
			}
			if (immediate>=-0x8000) {
				return TYPE_INDEX_IMM16S;
			}
			if (immediate>=-0x80000000L) {
				return TYPE_INDEX_IMM32S;
			}
		}
		return TYPE_INDEX_IMM64;
	}
	public static boolean isCompatibleType(int opType,int requiredOpType) {
		if (opType==requiredOpType) {
			return true;
		}
		if (opType>=TYPE_MAP.length || requiredOpType>=TYPE_MAP.length || opType<0 || requiredOpType<0) {
			return false;
		}
		TypeMapping mapping = TYPE_MAP[requiredOpType];
		if (mapping.subtypeIndices==null) {
			return false;
		}
		for (int subtype : mapping.subtypeIndices) {
			if (subtype==opType) {
				return true;
			}
		}
		return false;
	}

	public static boolean isCompatibleType(int type,ArrayList<Object> allowedTypes) {
		for (Object allowedType : allowedTypes) {
			if ((allowedType instanceof Integer) && isCompatibleType(type,(Integer)allowedType)) {
				return true;
			}
		}
		return false;
	}

	public static boolean isImmediateType(int type) {
		return (type>=FIRST_IMM_TYPE) && (type<=LAST_IMM_TYPE);
	}

	public static boolean isFunctionType(int type) {
		return (type==TYPE_INDEX_F16) || (type==TYPE_INDEX_F32) || (type==TYPE_INDEX_F64);
	}

	public static boolean isAddressReg(Register register, int mode) {
		if (mode==64) {
			return register.getType()==TYPE_INDEX_RG64;
		}
		else if (mode==32) {
			return register.getType()==TYPE_INDEX_RG32_32_64;
		}
		else {	// mode==16
			// NOTE: Only bx (3), bp (5), si (6), and di (7) are valid address registers in 16-bit mode.
			return register.getType()==TYPE_INDEX_RG16_32_64 && register.getNumber()>=3 && register.getNumber()!=4;
		}
	}

	public static boolean isPrimitiveType(String type) {
		return ASMLoad.instance.dataDeclarationKeywords.containsKey(type.toLowerCase()) || type.toLowerCase().startsWith("ptr ");
	}

	public static boolean containsRegisterType(int type) {
		return (type>=FIRST_REG_TYPE && type<=LAST_REG_TYPE) || (type>=FIRST_RM_TYPE && type<=LAST_RM_TYPE);
	}

	public static boolean containsMemoryType(int type) {
		return (type>=FIRST_MEM_TYPE && type<=LAST_MEM_TYPE) || (type>=FIRST_RM_TYPE && type<=LAST_RM_TYPE);
	}

	public static boolean isLabelType(int type) {
		return type==TYPE_INDEX_SHORT_LABEL || type==TYPE_INDEX_NEAR_LABEL;
	}

	public static class TypeMapping {
		public final String name;
		public final int[] subtypeIndices;
		/**
		 * Size in BITS, NOT BYTES!!!
		 */
		public final int size;
		public static final int UNKNOWN_SIZE = -1;
		private TypeMapping(String name,int[] subtypeIndices) {
			this(name,UNKNOWN_SIZE,subtypeIndices);
		}
		private TypeMapping(String name,int size,int[] subtypeIndices) {
			this.name = name;
			this.subtypeIndices = subtypeIndices;
			this.size = size;
		}
	}
}
